<!doctype html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="Embeded Chatbot">

    <title>Chat Bot</title>
    <link id="linkFavicon" rel="shortcut icon" type="image/x-icon" href="/favicon.ico">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.2/font/bootstrap-icons.css">

    <script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/markdown-it@13.0.1/dist/markdown-it.min.js"></script>
    <style>
        div.x-chat {
            position: relative;
            margin-bottom: 40px;
        }

        div.x-chat>img {
            position: absolute;
            left: 10px;
            top: 10px;
            width: 50px;
            height: 50px;
            background-size: cover;
        }

        img.x-chat-robot {
            background-image: url();
        }

        img.x-chat-user {
            background-image: url();
        }

        div.x-chat>div.x-chat-content {
            margin-left: 80px;
            padding-top: 10px;
            padding-bottom: 10px;
        }

        div.x-chat-content pre {
            padding: 5px;
            border-radius: 10px;
            background-color: rgb(248, 249, 250);
            border: 1px solid rgb(222, 226, 230);
        }
    </style>
    <script>
        window.OPENAI_URL = 'https://api.openai.com/v1/chat/completions';

        function loadSetting(key) {
            return localStorage.getItem(key) || '';
        }

        function saveSetting(key, value) {
            localStorage.setItem(key, value);
        }

        function appendChat(type, text, error = false) {
            let $chats = $('#chats');
            $chats.append('<div class="x-chat"><img class="x-chat-img"><div class="x-chat-content"></div></div>');
            let
                $chat = $chats.find('div.x-chat:last-child'),
                $img = $chat.find('img.x-chat-img'),
                $content = $chat.find('div.x-chat-content');
            $img.addClass('x-chat-' + type);
            if (error) {
                $content.append('<div class="alert alert-danger"></div>');
                $content.find('.alert').text(text);
            } else {
                $content.html(markdownit().render(text));
            }
        }

        async function postAPI(openai_token, requestData) {
            let response = await fetch(window.OPENAI_URL, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    'Authorization': 'Bearer ' + openai_token
                },
                body: JSON.stringify(requestData)
            });
            return response.json();
        }

        function createChat() {
            let
                btn = $('#submit'),
                content = $('#content').val().trim(),
                prompt = $('#prompt').val().trim(),
                openai_token = $('#openai_token').val().trim(),
                model = $('#model').val(),
                requestData = {
                    model: model,
                    messages: []
                };
            if (!content) {
                return showAlert('Content is required.');
            }
            if (!openai_token) {
                return showAlert('Token is required.');
            }
            if (prompt) {
                requestData.messages.push({
                    role: 'system',
                    content: prompt
                });
            }
            requestData.messages.push({
                role: 'user',
                content: content
            });
            btn.attr('disabled', 'disabled');
            $('#content').val('');
            appendChat('user', content);

            saveSetting('__prompt__', prompt);
            saveSetting('__openai_token__', openai_token);

            postAPI(openai_token, requestData)
                .then(responseData => {
                    if (responseData && responseData.error) {
                        let error = responseData.error;
                        appendChat('robot', (error && error.message) || 'An error occured.', true);
                    } else {
                        let
                            choices = responseData && responseData.choices,
                            choice = choices && choices[0],
                            text = choice && choice.message && choice.message.content;
                        appendChat('robot', text);
                    }
                }).catch((error) => {
                    console.error(error);
                    appendChat('robot', (error && error.message) || 'An error occured.', true);
                }).finally(() => {
                    btn.removeAttr('disabled');
                });
        }

        function showAlert(message) {
            $('#alert .modal-body').text(message);
            let m = new bootstrap.Modal('#alert');
            m.show();
        }

        $(function () {
            $('#prompt').val(loadSetting('__prompt__'));
            $('#openai_token').val(loadSetting('__openai_token__'));
        });
    </script>
</head>

<body>
    <div class="modal fade" id="alert" tabindex="-1">
        <div class="modal-dialog">
            <div class="modal-content">
                <div class="modal-header">
                    <h1 class="modal-title fs-5">Error</h1>
                    <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
                </div>
                <div class="modal-body">
                    ...
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">OK</button>
                </div>
            </div>
        </div>
    </div>

    <div class="container">
        <nav id="nav" class="navbar navbar-expand-lg navbar-light bg-light border-bottom"
            style="position:fixed; top:0; left:0; right:0; z-index: 99;">
            <div class="container">
                <a class="navbar-brand" href="/">
                    <i class="bi bi-robot"></i>
                    Chat Bot</a>
                <ul class="mr-2 navbar-nav">
                    <li class="nav-item">
                        <a href="https://github.com/michaelliao/chatbot" class="nav-link"><i
                                class="bi bi-file-earmark-text"></i>
                            Source</span></a>
                    </li>
                </ul>
            </div>
        </nav>
        <div style="padding-top: 80px;">
            <div class="row">
                <div class="col-12">
                    <h1>Chatbot Build on OpenAI</h1>
                    <div id="chats" class="mt-4 mb-4">
                    </div>
                    <div id="input">
                        <form>
                            <div class="mb-3">
                                <label for="content" class="form-label">Content:</label>
                                <textarea class="form-control" id="content" rows="5"
                                    style="resize: vertical;"></textarea>
                            </div>
                            <div class="mb-3">
                                <label for="prompt" class="form-label">Prompt:</label>
                                <input class="form-control" id="prompt">
                                <div id="promptHelp" class="form-text">Optional prompt for ChatGPT.</div>
                            </div>
                            <div class="mb-3">
                                <label for="openai_token" class="form-label">OpenAI Token:</label>
                                <input class="form-control" id="openai_token">
                                <div class="form-text">OpenAI API token. <a
                                        href="https://platform.openai.com/account/api-keys" target="_blank">Visit your
                                        API keys</a></div>
                            </div>
                            <div class="mb-3">
                                <label for="model" class="form-label">Model:</label>
                                <select id="model" class="form-select">
                                    <option selected>gpt-3.5-turbo</option>
                                    <option>gpt-3.5-turbo-0301</option>
                                </select>
                            </div>
                            <button onclick="createChat()" id="submit" type="button"
                                class="btn btn-primary">Submit</button>
                        </form>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <footer class="p-5 mt-5 bg-light border-top">
        <div class="container">
            <div class="row">
                <div class="col-12">
                    <div class="mb-4"><i class="bi bi-robot"></i> Embedded chatbot script</div>
                    <ul class="list-unstyled small text-muted">
                        <li class="mb-2">Designed and built by
                            <a target="_blank" href="https://github.com/michaelliao">Michael Liao
                                <i class="bi bi-box-arrow-up-right"></i></a>.
                        </li>
                        <li class="mb-2">Check the source code on
                            <a target="_blank" href="https://github.com/michaelliao/chatbot">Github
                                <i class="bi bi-box-arrow-up-right"></i></a>
                        </li>
                        <li class="mb-2">Code licensed
                            <a target="_blank" href="https://github.com/michaelliao/chatbot/blob/master/LICENSE">GPLv3
                                <i class="bi bi-box-arrow-up-right"></i></a>
                        </li>
                    </ul>
                </div>
            </div>
        </div>
    </footer>
</body>

</html>